package lock.synchronizedd;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

import java.util.concurrent.TimeUnit;

/**
 * @author ZhuQi
 * @ClassName Lock8Demo
 * @Date 2022/9/6 16:14
 * @Version 1.0
 * @Description 多线程 8锁案例
 * 1.标准访问有ab两个线程，请问先打印邮件还是短信
 * 2.sendEmail 这时暂停三秒，请问先打印邮件还是短信
 * 3.添加一个普通的 hello 方法，先打印邮件还是hello
 * 4.两部手机，请问先打印邮件还是短信
 * 5.一部手机，方法修改为静态方法，请问先打印邮件还是短信
 * 6.两部手机，方法修改为静态方法，请问先打印邮件还是短信
 * 7.一部手机，一个静态方法，一个普通同步方法，请问先打印邮件还是短信
 * 8.两部手机，一个静态方法，一个普通同步方法，请问先打印邮件还是短信
 */
@Slf4j
public class Lock8Demo {

    /**
     * 测试内容: 1.标准访问有ab两个线程，请问先打印邮件还是短信
     * 测试结果: 显输出邮件，再输出短信 因为加锁方式都是对象锁
     *          a 线程先执行，a先获取,b后获取这里能保证操作数据的顺序性
     */
    @Test
    public void test() throws Exception {
        Phone phone = new Phone();

        new Thread(phone::sendEmail,"a").start();

        // 保证 a 线程先启动
        TimeUnit.SECONDS.sleep(1);

        new Thread(phone::sendSms,"b").start();

        System.in.read();
    }

    /**
     * 测试内容: 2.sendEmail 这时暂停三秒，请问先打印邮件还是短信
     * 测试结果: 同样有序，因为是对象锁，两个线程对同一个对象加锁
     *
     */
    @Test
    public void test01() throws Exception {
        Phone phone = new Phone();

        new Thread(phone::sendEmail,"a").start();

        // 保证 a 线程先启动
        TimeUnit.SECONDS.sleep(1);

        new Thread(phone::sendSms,"b").start();

        System.in.read();
    }

    /**
     * 测试内容: 3.添加一个普通的 hello 方法，先打印邮件还是hello
     * 测试结果: 在 sendEmail 会阻塞3S的情况下大概率是hello,
     *          因为 hello 是无锁的，不需要顺序执行
     */
    @Test
    public void test02() throws Exception {
        Phone phone = new Phone();

        new Thread(phone::sendEmail,"a").start();

        // 保证 a 线程先启动
        TimeUnit.SECONDS.sleep(1);

        new Thread(phone::hello,"b").start();

        System.in.read();
    }

    /**
     * 测试内容: 4.两部手机，请问先打印邮件还是短信
     * 测试结果: 随机出现，加锁对象不是同一个，不保证有序性
     */
    @Test
    public void test03() throws Exception {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();

        new Thread(phone1::sendEmail,"a").start();

        // 保证 a 线程先启动
        TimeUnit.SECONDS.sleep(1);

        new Thread(phone2::sendSms,"b").start();

        System.in.read();
    }

    /**
     * 测试内容: 5.两部手机，方法修改为静态方法，请问先打印邮件还是短信
     * 测试结果: 当方法为static时 锁住的为类对象，类对象只有一个，所以这里
     *          也是有序执行
     */
    @Test
    public void test04() throws Exception {
        Phone2 phone = new Phone2();

        new Thread(() -> phone.sendEmail(),"a").start();

        // 保证 a 线程先启动
        TimeUnit.SECONDS.sleep(1);

        new Thread(() -> phone.sendSms(),"b").start();

        System.in.read();
    }

    /**
     * 测试内容: 6.两部手机，方法修改为静态方法，请问先打印邮件还是短信
     * 测试结果: 当方法为static时 锁住的为类对象，类对象只有一个，所以这里
     *          也是有序执行
     */
    @Test
    public void test05() throws Exception {
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();

        new Thread(() -> phone1.sendEmail(),"a").start();


        new Thread(() -> phone2.sendSms(),"b").start();

        System.in.read();
    }

    /**
     * 测试内容: 7.一部手机，一个静态方法，一个普通同步方法，请问先打印邮件还是短信
     * 测试结果: 锁住的对象不一样，一个是类对象，一个是普通对象
     *          所以这里无序
     */
    @Test
    public void test06() throws Exception {
        Phone2 phone1 = new Phone2();

        new Thread(() -> phone1.sendEmail(),"a").start();

        new Thread(() -> phone1.sendSms(),"b").start();

        System.in.read();
    }

    /**
     * 测试内容: 8.两部手机，一个静态方法，一个普通同步方法，请问先打印邮件还是短信
     * 测试结果: 锁住的对象不一样，一个是类对象，一个是普通对象
     *          所以这里无序
     */
    @Test
    public void test07() throws Exception {
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();

        new Thread(() -> phone1.sendEmail(),"a").start();

        new Thread(() -> phone2.sendSms(),"b").start();

        System.in.read();
    }
}

/**
 * 资源类
 */
@Slf4j
class Phone{
    public synchronized void sendEmail(){
        // 2 暂停3S
//        try {
//            TimeUnit.SECONDS.sleep(3);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        log.info("---sendEmail");
    }

    public synchronized void sendSms(){
        log.info("---sendSms");
    }

    public void hello(){
        log.info("---hello");
    }
}

@Slf4j
class Phone2{
    public static synchronized void sendEmail(){
        log.info("---sendEmail");
    }

    public static synchronized void sendSms(){
        log.info("---sendSms");
    }

    public void hello(){
        log.info("---hello");
    }
}

@Slf4j
class Phone3{

    private absClass absClass;

    private interfaceClass interfaceClass;

    private enumClass enumClass;

    private Phone2 Phone2;

    public static synchronized void sendEmail(){
        log.info("---sendEmail");
    }

    public synchronized void sendSms(){
        log.info("---sendSms");
    }

    public void hello(){
        synchronized (enumClass) {

        }
    }
}

abstract class absClass{}

interface interfaceClass{
}

enum enumClass{
}